package data.Table;

import java.io.FileNotFoundException;
import java.util.Iterator;
import java.util.List;


import file_rw.FileRW;

public class Page implements PageInterface{
	
	
	public int pageNum;
	public int flags;
	public int firstFreeBlock;
	public int numOfCells ;
	public int cellsOffset ;
	public int emptyPicesNum ;
	public int rightChild ;
	public byte[] pageByte = new byte[Table.table_page_defaultsize];
	
	public List<Integer> pointer;
	
	
	public void setPageFromByte(byte[] b){
		FileRW fr = new FileRW();
		flags = fr.readIntFromByte(b, 0, 1);
		firstFreeBlock = fr.readIntFromByte(b, 1, 2);
		numOfCells = fr.readIntFromByte(b, 3, 2);
		cellsOffset = fr.readIntFromByte(b, 5, 2);
		emptyPicesNum = fr.readIntFromByte(b, 7, 1);
		if(flags == 0||flags == 1){
			rightChild =  fr.readIntFromByte(b, 8, 4);
		}
		
		for(int i = 0;i<numOfCells;i++){
			pointer.add(fr.readIntFromByte(pageByte, 12+i*2,2 ));
		}
	}
	
	public Page(int n){
		pageNum = n;	
		pageByte = read();
		setPageFromByte(pageByte);
	}
	public Page(int n,boolean check){
		pageNum = n;
	}
	
	
	public int getPageNum() {
		return pageNum;
	}

	public void setPageNum(int pageNum) {
		this.pageNum = pageNum;
	}

	public int getFlags() {
		return flags;
	}

	public void setFlags(int flags) {
		this.flags = flags;
	}

	public int getFirstFreeBlock() {
		return firstFreeBlock;
	}

	public void setFirstFreeBlock(int firstFreeBlock) {
		this.firstFreeBlock = firstFreeBlock;
	}

	public int getNumOfCells() {
		return numOfCells;
	}

	public void setNumOfCells(int numOfCells) {
		this.numOfCells = numOfCells;
	}

	public int getCellsOffset() {
		return cellsOffset;
	}

	public void setCellsOffset(int cellsOffset) {
		this.cellsOffset = cellsOffset;
	}

	public int getEmptyPicesNum() {
		return emptyPicesNum;
	}

	public void setEmptyPicesNum(int emptyPicesNum) {
		this.emptyPicesNum = emptyPicesNum;
	}

	public int getRightChild() {
		return rightChild;
	}

	public void setRightChild(int rightChild) {
		this.rightChild = rightChild;
	}

	public List<Integer> getPointer() {
		return pointer;
	}

	public void setPointer(List<Integer> pointer) {
		this.pointer = pointer;
	}

	
	
	/* (non-Java doc)
	 * read the head form the pageByte; 
	 * @see data.Table.PageInterface#readHead()
	 */
	@Override
	public byte[] readHead() {
		
		if(flags == 1){
			byte[] temp = new byte[page_leafsize];
			for(int i =0;i<page_leafsize;i++){
				temp[i] = pageByte[i];
			}
			return temp;
		}else {
			byte[] temp = new byte[page_internalsize];
			for(int i =0;i<page_internalsize;i++){
				temp[i] = pageByte[i];
			}
			return temp;			
		}	
	}


	
	/**
	 * get the pointer[i] from the pageByte[]
	 * @param i
	 * @return
	 */
	public int getPointerFromPageByte(int i){
		int offset = 0;
		if(flags == 1){
			offset  = page_leafsize + (i-1)*page_pointersize; 
		}
		byte[] temp = new byte[4];
		temp[0] = 0;
		temp[1] = 0;
		temp[2] = pageByte[offset+1];
		temp[3] = pageByte[offset+2];
		int res = 0;
		res = FileRW.byte2int(temp);
		return res ;		
	}

	/* (non-Java doc)
	 * get pointer[] from the pageByte[]
	 * @see data.Table.PageInterface#readPointer()
	 */
	@Override
	public int[] readPointer() {

		int i =0;
		int n = numOfCells;
		int[] pointer = new int[n];
		for(i=0;i<n;i++){
			pointer[i] = getPointerFromPageByte(i);
		}
		return pointer;
	}

	/* (non-Java doc)
	 * read Table.table_page_defaultsize bytes from the filePath of pageNum
	 * @see data.Table.PageInterface#read()
	 */
	public byte[] read() {

		byte[] read = new byte[Table.table_page_defaultsize];
		FileRW fr = new FileRW();
		try {
			read = fr.readBytes(filePath, Table.table_head_size+(pageNum-1)*Table.table_page_defaultsize, read.length);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		return read;
	}
	
	public void ini(){
		
		 flags = 0;
		 firstFreeBlock = 0;
		 numOfCells = 0;
		 cellsOffset = Table.table_page_defaultsize;
		 emptyPicesNum = 0;
		 rightChild = 0 ;				 
		 writePageByte();
	}
	
	public void ini(int type){
		 flags = type;
		 firstFreeBlock = 0;
		 numOfCells = 0;
		 cellsOffset = Table.table_page_defaultsize;
		 emptyPicesNum = 0;
		 rightChild = 0 ;				 
		 writePageByte();
	}
	
	public void ini(boolean overflow){
		 flags = 0;
		 firstFreeBlock = 0;
		 numOfCells = 0;
		 cellsOffset = 0;
		 emptyPicesNum = 0;
		 rightChild = 0 ;
		 writePageByte();
	}
	

	/**
	 * write the pageByte to file
	 */
	public void writePageByte(){
		setPageByte();
		FileRW fw = new FileRW();
		try {
			fw.writeBytes(filePath, Table.table_head_size+(pageNum-1)*Table.table_page_defaultsize, pageByte);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	

	/**
	 * write the var to pageByte[]
	 */
	public void setPageByte(){
		FileRW fr = new FileRW();
		pageByte = fr.addByte(pageByte, flags, 0, 1);
		pageByte = fr.addByte(pageByte, firstFreeBlock, 1, 2);
		pageByte = fr.addByte(pageByte, numOfCells, 3, 2);
		pageByte = fr.addByte(pageByte, cellsOffset , 5, 2);
		pageByte = fr.addByte(pageByte, emptyPicesNum, 7, 1);
		pageByte = fr.addByte(pageByte, rightChild, 8, 4);
	}
	
	/**
	 * give the start block (at offset of n),return the next block
	 * @param n
	 * @return
	 */
	public int getNextFreeBlock(int n){
		int res = 0;
		byte[] temp = new byte[4];
		temp[0] = 0;
		temp[1] = 0;
		temp[2] = pageByte[n];
		temp[3] = pageByte[n+1];
		res = FileRW.byte2int(temp);
		return res;
	}
	
	/**
	 * at the start n ,get the block length
	 * @param n
	 * @return
	 */
	public int getBlockLength(int n){
		int len = 0;
		byte[] temp = new byte[4];
		temp[0] = 0;
		temp[1] = 0;
		temp[2] = pageByte[n+2];
		temp[3] = pageByte[n+3];
		len = FileRW.byte2int(temp);
		return len;
	}
	
	/**
	 * get the first block which can store the byte[] of length,if have no return 0
	 * @param len
	 * @return
	 */
	public int getFirstStoreBlock(int len){
		int res = 0;
		if(firstFreeBlock==0){
			return res;
		}else{
			int tem = firstFreeBlock;
			while(getBlockLength(tem)<len){
				tem = getNextFreeBlock(tem);
				if(getBlockLength(tem) == 0){
					return 0;
				}
			}
			return tem;
		}
	}
	
	/**
	 * 
	 * from the start of n(cells),get the id (2+2+2*number)
	 * @param n
	 * @return
	 */
	public int getIdFromByte(int n){
		int res = 0;
		int num = 0;		
		byte[] temp = new byte[4];
		temp[0] = 0;
		temp[1] = 0;
		temp[2] = pageByte[n+2];
		temp[3] = pageByte[n+3];
		num = FileRW.byte2int(temp);
		temp[0] = pageByte[n+4+num*2];
		temp[1] = pageByte[n+5+num*2];
		temp[2] = pageByte[n+6+num*2];
		temp[3] = pageByte[n+7+num*2];
		res =  FileRW.byte2int(temp);		
		return res;
	}
	
	/**
	 * 
	 * sort the pointer from small to big
	 */
	public void sort(){
		List<Integer> temp = pointer;
		int[] b = new int[temp.size()];
		int[] num = new int[temp.size()];
		int i = 0;
		Iterator<Integer> it = temp.iterator();
		while(it.hasNext()){
			b[i++] = (int)it.next();
		}		
		for(int j =0;i<b.length;j++){
			num[j] = getIdFromByte(b[j]);
		}
		for(int ii=0;ii<num.length;ii++){
			for(int jj=ii;jj<num.length;jj++){
				if(num[jj]<num[ii]){
					int temnum = num[ii];
					int temb = b[ii];
					num[ii] = num[jj];
					b[ii] = b[jj];
					num[jj] = temnum;
					b[jj] = temb;
				}
			}
		}
		temp.clear();
		for(int j=0;j<b.length;j++){
			temp.add(b[j]);
		}
		pointer = temp;		
	}
	
	/**
	 * insert byte[] b into the page
	 * @param b
	 * @return
	 */
	public boolean insert(byte[] b){
		int len = b.length;
		int tem = 0 ;
		tem = getFirstStoreBlock(firstFreeBlock);
		if(tem == 0){
			if(cellsOffset>=page_internalsize+numOfCells*2+len+numOfCells){
				
				for(int i =len-1,j=cellsOffset-1;i>=0;i--,j--){
					pageByte[j] = b[i];
				}
				cellsOffset = cellsOffset - len;
			    pointer.add(cellsOffset+1);
			    numOfCells ++;
			    sort();
			    return true;
			}else {
				return false;
			}
		}else {
			if(tem == firstFreeBlock){
				int other = 0;
				other = getBlockLength(tem);
				emptyPicesNum = emptyPicesNum + other - len; 
				firstFreeBlock = getNextFreeBlock(tem);
				pointer.add(tem);
				numOfCells ++;
				for(int i=0,j = tem;i<len;i++,j++){
					pageByte[j] = b[i];
				}
				sort();
				return true;
			}else{
				int temp = firstFreeBlock;
				while(getNextFreeBlock(temp)!=tem){
					temp = getNextFreeBlock(temp);
				}
				int tems = getNextFreeBlock(tem);
				if(tems ==0){
					pageByte[temp] = 0;
					pageByte[temp+1] = 0;
				}else {
					pageByte[temp] = pageByte[tems];
					pageByte[temp+1] = pageByte[tems+1];
				}
				pointer.add(tem);
				numOfCells ++;
				for(int i=0,j = tem;i<len;i++,j++){
					pageByte[j] = b[i];
				}
				sort();
				return true;
			}
		}		
	}	
	
	

}
